---
title: Tabbable
description: Maintain a consistent tab order for tabbable elements.
---

<ComponentPreview name="playground-demo" id="tabbable" />

<PackageInfo>

## Features

- Ensures consistent tab order between tabbable elements in the editor.

</PackageInfo>

## Installation

```bash
npm install @udecode/plate-tabbable
```

## Usage

```tsx
import { TabbablePlugin } from '@udecode/plate-tabbable/react';

const plugins = [
  // ...otherPlugins,
  TabbablePlugin,
];
```

### Conflicts with other plugins

The Tabbable plugin may cause issues with other plugins that handle the `Tab` key, such as:

- Lists
- Code blocks
- Indent plugin

Use the `query` option to disable the Tabbable plugin when the `Tab` key should be handled by another plugin:

```tsx
query: (editor) => {
  const inList = findNode(editor, { match: { type: ListItemPlugin.key } });
  const inCodeBlock = findNode(editor, { match: { type: CodeBlockPlugin.key } });
  return !inList && !inCodeBlock;
},
```

Alternatively, if you're using the Indent plugin, you can enable the Tabbable plugin only when a specific type of node is selected, such as voids:

```tsx
query: (editor) => !!findNode(editor, {
  match: (node) => isVoid(editor, node),
}),
```

### Non-void Slate nodes

One `TabbableEntry` will be created for each tabbable DOM element in the editor, as determined using the <Link href="https://www.npmjs.com/package/tabbable">tabbable</Link> NPM package. The list of tabbables is then filtered using `isTabbable`.

By default, `isTabbable` only returns true for entries inside void Slate nodes. You can override `isTabbable` to add support for DOM elements contained in other types of Slate node.

```tsx
// Enable tabbable DOM elements inside CUSTOM_ELEMENT
isTabbable: (tabbableEntry) => (
  tabbableEntry.slateNode.type === CUSTOM_ELEMENT ||
  isVoid(editor, tabbableEntry.slateNode)
),
```

### DOM elements outside the editor

In some circumstances, you may want to allow users to tab from the editor to a DOM element rendered outside the editor, such as an interactive popover.

To do this, override `insertTabbableEntries` to return an array of `TabbableEntry` objects, one for each DOM element outside the editor that you want to include in the tabbable list. The `slateNode` and `path` of the `TabbableEntry` should refer to the Slate node the user's cursor will be inside when the DOM element should be tabbable to. For example, if the DOM element appears when a link is selected, the `slateNode` and `path` should be that of the link.

Set the `globalEventListener` option to `true` to make sure the Tabbable plugin is able to return the user's focus to the editor.

```tsx
// Add buttons inside .my-popover to the list of tabbables
globalEventListener: true,
insertTabbableEntries: (editor) => {
  const [selectedNode, selectedNodePath] = getNodeEntry(editor, editor.selection);

  return [
    ...document.querySelectorAll('.my-popover > button'),
  ].map((domNode) => ({
    domNode,
    slateNode: selectedNode,
    path: selectedNodePath,
  }));
},
```

## Plugins

### TabbablePlugin

<APIOptions>
<APIItem name="query" type="(editor: PlateEditor<V>, event: KeyboardEvent) => boolean" optional>
Dynamically enables or disables the plugin. Returns `true` by default.

- **Default:** **`() => true`**

</APIItem>
<APIItem name="globalEventListener" type="boolean" optional>
Determines if the plugin adds its event listener to the document instead of the editor, which allows it to capture events from outside the editor.

- **Default:** **`false`**

</APIItem>
<APIItem name="insertTabbableEntries" type="(editor: PlateEditor<V>, event: KeyboardEvent) => TabbableEntry[]" optional>
Adds additional tabbable entries to the list of tabbable elements. Useful for adding tabbables that are not contained within the editor. It ignores `isTabbable`.

- **Default:** **`() => []`**

</APIItem>
<APIItem name="isTabbable" type="(tabbableEntry: TabbableEntry) => boolean" optional>
Determines whether an element should be included in the tabbable list. Returns `true` if the tabbableEntry's slateNode is void.

- **Default:** **`(tabbableEntry) => isVoid(editor, tabbableEntry.slateNode)`**

</APIItem>
</APIOptions>

## API

### TabbableEntry

Defines the properties of a tabbable entry.

<APIAttributes>
  <APIItem name="domNode" type="HTMLElement">
    The HTML element that represents the tabbable entry.
  </APIItem>
  <APIItem name="slateNode" type="TNode">
    The corresponding Slate node of the tabbable entry.
  </APIItem>
  <APIItem name="path" type="TPath">
    The path to the Slate node in the Slate document.
  </APIItem>
</APIAttributes>